﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using ABMath.IridiumExtensions;
using ABMath.Miscellaneous;
using ABMath.ModelFramework.Models;
using MathNet.Numerics.Distributions;
using MathNet.Numerics.LinearAlgebra;
using NUnit.Framework;

namespace ABMath.Tests.IridiumExtension
{
    [TestFixture]
    public class MonteCarloStatisticsTest
    {
        private Matrix[] T;

        private StandardDistribution stdNormal;
        private MVNormalDistribution mvNormal;


        [SetUp]
        public void Setup()
        {
            T = new Matrix[50];
            for (int i = 2; i < 50; ++i)
                T[i] = BStatistic.BuildAMatrix(i);

            stdNormal = new StandardDistribution();
        }

        private void SaveXml(Matrix quants, string fileName)
        {
            double[] values = new double[quants.RowCount];
            for (int i = 0; i < quants.RowCount; ++i)
                values[i] = quants[i, 1];

            var writer = new StreamWriter(fileName);
            var serializer = new XmlSerializer(typeof(double[]));
            serializer.Serialize(writer, values);
            writer.Close();            
        }

        private Matrix LoadXml(string fileName)
        {
            var reader = new StreamReader(fileName);
            var serializer = new XmlSerializer(typeof (double[]));
            var o = serializer.Deserialize(reader) as double[];
            reader.Close();

            double resolution = 1.0/o.Length;
            var quants = new Matrix(o.Length, 2);
            for (int i=0 ; i<o.Length ; ++i)
            {
                quants[i, 0] = (i + 0.5)*resolution;
                quants[i, 1] = o[i];
            }
            return quants;
        }

        [Test]
        public void Simulate()
        {
            for (int order = 4; order <= 40; order += 4)
            {
                const int numIterations = 400000;

                var sims = BuildArray(order, numIterations);
                var quants = GetQuantiles(sims, 0.005);

                var fnm = string.Format("c:\\temp\\quants{0:0}.xml", order);

                SaveXml(quants, fnm);
                var da = new DistributionApproximation();

                var reader = new StreamReader(fnm);
                da.LoadFromXmlArray(reader);
                reader.Close();
                // var quants2 = LoadXml(fnm);
            }
        }

        private Matrix GetQuantiles(double[] values, double resolution)
        {
            int n = values.Length;
            int sz = 0;
            for (double quant = resolution / 2.0; quant < 1.0; quant += resolution)
                ++sz;
            var quants = new Matrix(sz, 2);
            for (int i = 0; i < sz; ++i )
            {
                double quant = (i + 0.5)*resolution;
                int idx = (int)Math.Round(n * quant);
                quants[i, 0] = quant;
                quants[i, 1] = values[idx];
            }
            return quants;
        }

        private double[] BuildArray(int order, int numIterations)
        {
            mvNormal = new MVNormalDistribution() { Mu = new Vector(order), Sigma = Matrix.Identity(order,order) };

            var sims = new double[numIterations];

            for (int i=0 ; i<numIterations ; ++i)
            {
                var tv = mvNormal.NextVector();
                tv = T[order].MultiplyBy(tv);
                double maxVal = tv.Max();
                double minVal = tv.Min();
                sims[i] = Math.Max(maxVal, -minVal);
            }

            Array.Sort(sims);
            return sims;
        }
    }
}
